using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.IO;

namespace CustomDataGrid
{
	public class Form1 : System.Windows.Forms.Form
	{
		#region Fields
		//created by designer 
		private System.Windows.Forms.DataGrid dataGrid1;
		private CustomDataGrid.DataSet1 dataSet11;
		private System.ComponentModel.Container components = null;

		// Used to refer to index specific columns in our grid.
		// Values set in CreateTheTable method.
		private int unitsInStockCol;
		private int unitsOnOrderCol;
		private int reorderLevelCol;
		private int checkCol;
		private int nameCol;

		// Cached GDI+ objects created in Form1_Load.
		// Used in the event handlers for the custom column styles events.
		private Brush disabledBackBrush;
		private Brush disabledTextBrush;
		private Brush alertBackBrush;
		private Font alertFont;
		private Brush alertTextBrush;
		private Font currentRowFont;
		private Brush currentRowBackBrush;

		// Grid tootips-fields used by the grid's mousemove events to manage
		//  row specific tips, initialized in Form_Load, used in dataGrid1_MouseMove.
		private int hitRow;
		private System.Windows.Forms.ToolTip toolTip1;
		#endregion

		#region Wizard Generated Code
		public Form1()
		{
			InitializeComponent();
		}

		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if (components != null) 
				{
					components.Dispose();
					//dispose of our cached graphics objects
					if(disabledBackBrush != null)
					{
						disabledBackBrush.Dispose();
						disabledTextBrush.Dispose();
						alertBackBrush.Dispose();
						alertFont.Dispose();
						alertTextBrush.Dispose();
						currentRowFont.Dispose();
						currentRowBackBrush.Dispose();
					}
				}
			}
			base.Dispose( disposing );
		}

		#region Windows Form Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
            this.dataGrid1 = new System.Windows.Forms.DataGrid();
            this.dataSet11 = new CustomDataGrid.DataSet1();
            ((System.ComponentModel.ISupportInitialize)(this.dataGrid1)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.dataSet11)).BeginInit();
            this.SuspendLayout();
            // 
            // dataGrid1
            // 
            this.dataGrid1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
                        | System.Windows.Forms.AnchorStyles.Left)
                        | System.Windows.Forms.AnchorStyles.Right)));
            this.dataGrid1.CaptionText = "ReOrder Grid";
            this.dataGrid1.DataMember = "";
            this.dataGrid1.DataSource = this.dataSet11.Products;
            this.dataGrid1.HeaderForeColor = System.Drawing.SystemColors.ControlText;
            this.dataGrid1.Location = new System.Drawing.Point(24, 24);
            this.dataGrid1.Name = "dataGrid1";
            this.dataGrid1.Size = new System.Drawing.Size(440, 216);
            this.dataGrid1.TabIndex = 0;
            this.dataGrid1.CurrentCellChanged += new System.EventHandler(this.dataGrid1_CurrentCellChanged);
            this.dataGrid1.Click += new System.EventHandler(this.dataGrid1_Click);
            // 
            // dataSet11
            // 
            this.dataSet11.DataSetName = "DataSet1";
            this.dataSet11.Locale = new System.Globalization.CultureInfo("en-US");
            this.dataSet11.SchemaSerializationMode = System.Data.SchemaSerializationMode.IncludeSchema;
            // 
            // Form1
            // 
            this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
            this.ClientSize = new System.Drawing.Size(488, 261);
            this.Controls.Add(this.dataGrid1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.Load += new System.EventHandler(this.Form1_Load);
            ((System.ComponentModel.ISupportInitialize)(this.dataGrid1)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.dataSet11)).EndInit();
            this.ResumeLayout(false);

		}
		#endregion

		[STAThread]
		static void Main() 
		{
			Application.Run(new Form1());
		}
		#endregion

		#region Initialization Code
		//Used to:
		//	Load the table.
		//	Set a tablestyle into the grid.
		//	Assign the grid's datasource.
		//	Create & cache some graphics objects.
		private void Form1_Load(object sender, System.EventArgs e)
		{
			//read the data
			this.dataSet11.ReadXml(GetPathTo("ProductsTable.XML"));

			//set the data source
			DataTable dt = this.dataSet11.Tables["Products"];
            this.dataGrid1.DataSource = dt;

			//add the tablestyle to the datagrid
			this.dataGrid1.TableStyles.Add(CreateTheTable());

			//create and cash some GDI+ objects that we are continually using
			//in our cell formatting.
			this.disabledBackBrush = new SolidBrush(SystemColors.InactiveCaptionText);
			this.disabledTextBrush = new SolidBrush(SystemColors.GrayText);
			
			this.alertBackBrush = new SolidBrush(SystemColors.HotTrack);
			this.alertFont = new Font(this.dataGrid1.Font.Name, this.dataGrid1.Font.Size, FontStyle.Bold);
			this.alertTextBrush = new SolidBrush(Color.White);
			
			this.currentRowFont = new Font(this.dataGrid1.Font.Name, this.dataGrid1.Font.Size, FontStyle.Bold);
			this.currentRowBackBrush = new SolidBrush(Color.FromArgb(255, 200, 200));

			//set up the grid tooltips
			this.hitRow = -1;
			this.toolTip1 = new ToolTip();
			this.toolTip1.InitialDelay = 300;
			this.dataGrid1.MouseMove += new MouseEventHandler(dataGrid1_MouseMove);
		}
	
		private DataGridTableStyle CreateTheTable()
		{
			//create an empty DataGridTableStyle & set mapping name to table
			DataGridTableStyle tableStyle = new DataGridTableStyle();
			tableStyle.MappingName = "Products";

			//now create custom DataGridColumnSyle for each col we want to 
			//appear in the grid and in the order that we want to see them

			//Discontinued
			FormattableBooleanColumn discontinuedCol = new FormattableBooleanColumn();
			discontinuedCol.MappingName = "Discontinued";
			discontinuedCol.HeaderText = "";
			discontinuedCol.Width = 30;
			discontinuedCol.AllowNull = false; // turn off tristate
			this.checkCol = 0;

			discontinuedCol.SetCellFormat += new FormatCellEventHandler(SetCellFormat);
			discontinuedCol.BoolValueChanged += new BoolValueChangedEventHandler(BoolValueChanged);
			tableStyle.GridColumnStyles.Add(discontinuedCol);

			//ProductID
			FormattableTextBoxColumn column = new FormattableTextBoxColumn();
			column.MappingName = "ProductID";
			column.HeaderText = "ID";
			column.Width = 30;
			column.SetCellFormat += new FormatCellEventHandler(SetCellFormat);
			tableStyle.GridColumnStyles.Add(column);

			//ProductName
			column = new FormattableTextBoxColumn();
			column.MappingName = "ProductName";
			column.HeaderText = "Name";
			column.Width = 140;
			column.SetCellFormat += new FormatCellEventHandler(SetCellFormat);
			tableStyle.GridColumnStyles.Add(column);
			this.nameCol = 2;

			//   lets hide the next 2 columns by not adding a 
			//   GridColumnStyle to the tableStyle

//			//SupplierID
//			column = new FormattableTextBoxColumn();
//			column.MappingName = "SupplierID";
//			column.HeaderText = "SupplierID";
//			column.SetCellFormat += new FormatCellEventHandler(SetCellFormat);
//			tableStyle.GridColumnStyles.Add(column);

//			//CategoryID
//			column = new FormattableTextBoxColumn();
//			column.MappingName = "CategoryID";
//			column.HeaderText = "CategoryID";
//			column.SetCellFormat += new FormatCellEventHandler(SetCellFormat);
//			tableStyle.GridColumnStyles.Add(column);

			//QuantityPerUnit
			column = new FormattableTextBoxColumn();
			column.MappingName = "QuantityPerUnit";
			column.HeaderText = "QuantityPerUnit";
			column.SetCellFormat += new FormatCellEventHandler(SetCellFormat);
			tableStyle.GridColumnStyles.Add(column);

			//UnitPrice
			column = new FormattableTextBoxColumn();
			column.MappingName = "UnitPrice";
			column.HeaderText = "UnitPrice";
			column.SetCellFormat += new FormatCellEventHandler(SetCellFormat);
			tableStyle.GridColumnStyles.Add(column);

			//UnitsInStock
			column = new FormattableTextBoxColumn();
			column.MappingName = "UnitsInStock";
			column.HeaderText = "UnitsInStock";
			column.SetCellFormat += new FormatCellEventHandler(SetCellFormat);
			tableStyle.GridColumnStyles.Add(column);
			this.unitsInStockCol = 5;

			//UnitsOnOrder
			column = new FormattableTextBoxColumn();
			column.MappingName = "UnitsOnOrder";
			column.HeaderText = "UnitsOnOrder";
			column.SetCellFormat += new FormatCellEventHandler(SetCellFormat);
			tableStyle.GridColumnStyles.Add(column);
			this.unitsOnOrderCol = 6;

			//ReorderLevel
			column = new FormattableTextBoxColumn();
			column.MappingName = "ReorderLevel";
			column.HeaderText = "ReorderLevel";
			column.SetCellFormat += new FormatCellEventHandler(SetCellFormat);
			tableStyle.GridColumnStyles.Add(column);
			this.reorderLevelCol = 7;
			
			return tableStyle;
		}
		

		//look back up in the project path for file...
		private string GetPathTo(string name)
		{
			for(int i = 0; i < 3; ++i)
			{
				if(File.Exists(name))
					break;
				name = @"..\" + name;
			}
			return name;
		}


		#endregion

		#region Custom ColumnStyle Events

		// Provides the format for the given cell.
		private void SetCellFormat(object sender, DataGridFormatCellEventArgs e)
		{
			//conditionally set properties in e depending upon e.Row and e.Col
			bool discontinued = (bool) ((e.Column != 0) ? this.dataGrid1[e.Row, 0] : e.CurrentCellValue);
			//check is discontinued
			if(e.Column > 0 && (bool)(this.dataGrid1[e.Row, 0]))//discontinued)
			{
				e.BackBrush = this.disabledBackBrush;
				e.ForeBrush = this.disabledTextBrush;
			}
			else if(e.Column > 0 && e.Row == this.dataGrid1.CurrentRowIndex)//discontinued)
			{
				e.BackBrush = this.currentRowBackBrush;
				e.TextFont = this.currentRowFont;
			}
			else if(NeedToReorder(e.Row))
			{
				e.TextFont = this.alertFont;
				e.ForeBrush = this.alertTextBrush;
				e.BackBrush = this.alertBackBrush;
			}
		}

		// Handles the changing of a boolvalue: 
		//		Forces the value committed and then redraws the row.
		//		The value is committed so we can test it in our drawing 
		//				code by just accessing the DataGridValue.
		private void BoolValueChanged(object sender, BoolValueChangedEventArgs e)
		{
			this.dataGrid1.EndEdit(this.dataGrid1.TableStyles[0].GridColumnStyles["Discontinued"],e.Row, false);
			RefreshRow(e.Row);
			this.dataGrid1.BeginEdit(this.dataGrid1.TableStyles[0].GridColumnStyles["Discontinued"],e.Row);
		}
		#endregion

		#region ToolTip Implementation

		// Checks mouse position to see if it is over a discontinued row, 
		//        or reorder row. If so, set the tiptext and reset the tooltip.
		private void dataGrid1_MouseMove(object sender, MouseEventArgs e)
		{
			DataGrid.HitTestInfo hti = this.dataGrid1.HitTest(new Point(e.X, e.Y));
			BindingManagerBase bmb = this.BindingContext[this.dataGrid1.DataSource, this.dataGrid1.DataMember];
			if(hti.Row < bmb.Count && hti.Type == DataGrid.HitTestType.Cell 
				&&  hti.Row != hitRow  )
			{	
				if(this.toolTip1 != null && this.toolTip1.Active)
					this.toolTip1.Active = false; //turn it off

				string tipText = "";
				if((bool)this.dataGrid1[hti.Row, checkCol])
					tipText = this.dataGrid1[hti.Row, nameCol].ToString() + " discontinued";
				else if	(this.NeedToReorder(hti.Row))
				{
					tipText = this.dataGrid1[hti.Row, nameCol].ToString() + " order NOW";
				}
				if(tipText != "")
				{
					//new hit row
					hitRow = hti.Row;
					
					this.toolTip1.SetToolTip(this.dataGrid1, tipText);
					this.toolTip1.Active = true; //make it active so it can show itself
				}
				else
				hitRow = -1;
			}
		}
		#endregion

		#region OneClickBoolColumn Implementation
		
		// If there is a click on a dicontinued row, want the checkbox
		//      to be the active currentcell.
		// afterCurrentCellChanged is set in dataGrid1_CurrentCellChanged.
		private void dataGrid1_Click(object sender, System.EventArgs e)
		{
			Point pt = this.dataGrid1.PointToClient(Control.MousePosition);
			DataGrid.HitTestInfo hti = this.dataGrid1.HitTest(pt);
			BindingManagerBase bmb = this.BindingContext[this.dataGrid1.DataSource, this.dataGrid1.DataMember];
			if(afterCurrentCellChanged && hti.Row < bmb.Count 
				&& hti.Type == DataGrid.HitTestType.Cell 
				&&  hti.Column == checkCol )
			{	
				this.dataGrid1[hti.Row, checkCol] = !(bool)this.dataGrid1[hti.Row, checkCol];
				RefreshRow(hti.Row);
			}
			afterCurrentCellChanged = false;
		}
		#endregion

		#region Helper Methods

		// Forces a repaint of given row.
		private void RefreshRow(int row)
		{
			Rectangle rect = this.dataGrid1.GetCellBounds(row, 0);
			rect = new Rectangle(rect.Right, rect.Top, this.dataGrid1.Width, rect.Height);
			this.dataGrid1.Invalidate(rect);
		}

		// Given a row, computes whether product needs tobe reordered.
		private bool NeedToReorder(int row)
		{
			int unitsInStock = (Int16) this.dataGrid1[row,  this.unitsInStockCol];
			int unitsOnOrder = (Int16) this.dataGrid1[row,  this.unitsOnOrderCol];
			int reorderLevel = (Int16) this.dataGrid1[row,  this.reorderLevelCol];

			return (reorderLevel > (unitsInStock + unitsOnOrder));
		}

		//DataGrid.CurrentCellChanged used to:
		//	Force current cell to bool column if click on discontinued row.
		//	Set afterCurrentCellFlag which is part of the OneClickBoolColumn.
		private bool afterCurrentCellChanged = false;
		private void dataGrid1_CurrentCellChanged(object sender, System.EventArgs e)
		{
			//if click on a discontinued row, then set currrentcell to checkbox
			if((bool)this.dataGrid1[this.dataGrid1.CurrentRowIndex, checkCol])
				this.dataGrid1.CurrentCell = new DataGridCell(this.dataGrid1.CurrentRowIndex, checkCol);
			afterCurrentCellChanged = true;
		}
		#endregion
	}
}
